package com.bulk.demo.insert.controller;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bulk.demo.insert.entity.Student;
import com.bulk.demo.insert.mapper.StudentMapper;
import com.bulk.demo.insert.service.IStudentService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 几种批量插入的速度差异对比
 *
 * 测试环境：MySQL 5.7 、1M 带宽、30个字段无索引、500/6000条数据、 i7-9750H
 * 1. Mybatis xml  values <foreach></foreach>
 * 2. MyBatisPlus saveBatch
 * 3. MyBatisPlus insertBatchSomeColumn
 * 4. JDBC 批量 addBatch() executeBatch()
 *
 * @author 散装java
 * @date 2022-09-16
 */
@RestController
@AllArgsConstructor
public class StudentController {
    IStudentService studentService;
    StudentMapper studentMapper;

    @GetMapping("/mySaveBatch")
    public String mySaveBatch() {
        String str = this.run10((list) -> studentService.mySaveBatch(list));
        return "Mybatis xml  values耗时: " + str;
    }

    @GetMapping("/plusSaveBatch")
    public String plusSaveBatch() {
        String str = this.run10((list) -> studentService.saveBatch(list));
        return "MyBatisPlus saveBatch耗时：" + str;
    }

    @GetMapping("/plusInsertBatchSomeColumn")
    public String insertBatchSomeColumn() {
        String str = this.run10((list) -> studentMapper.insertBatchSomeColumn(list));
        return "MyBatisPlus insertBatchSomeColumn耗时：" + str;
    }

    @GetMapping("/saveBatchByJDBC")
    public String saveBatchByJDBC() {
        String str = this.run10((list) -> studentService.saveBatchByJDBC(list));
        return "jdbc batch耗时：" + str;
    }

    public String run10(Consumer<List<Student>> consumer) {
        List<Student> stuList = buildList();
        List<Long> list = new ArrayList<>();
        consumer.accept(stuList);
        for (int i = 0; i < 10; i++) {
            stuList = buildList();
            long l = System.currentTimeMillis();
            consumer.accept(stuList);
            long l1 = System.currentTimeMillis();
            list.add(l1 - l);
        }
        Long max = list.stream().max(Long::compareTo).orElse(0L);
        Long min = list.stream().min(Long::compareTo).orElse(0L);
        Double avg = list.stream().collect(Collectors.averagingLong(a -> a));
        list.sort(Long::compareTo);

        String str = String.format("30个字段，500条数据，10次插入数据结果：最大值: 【%s】, 最小值: 【%s】, 平均值：【%s】, 全部数据：%s", max, min, avg, list);
        System.out.println(str);
        return str;
    }

    List<Student> buildList() {
        // Packet for query is too large (6,470,812 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.
        ArrayList<Student> list = new ArrayList<>();
        for (int i = 0; i < 500; i++) {
            Student student = new Student();
            student.setName("散装java" + i);
            student.setSex(1);
            student.setAddress("江苏省南京市" + i);
            student.setTel("1786367" + i);
            student.setEmail(i + "@163.com");
            student.setRemark("不要996，祝各位发财！");
            student.setAddTime(LocalDateTime.now());
            student.setUpdateTime(LocalDateTime.now());
            student.setColumn10("setColumn10" + i);
            student.setColumn11("setColumn11" + i);
            student.setColumn12("setColumn12" + i);
            student.setColumn13("setColumn13" + i);
            student.setColumn14("setColumn14" + i);
            student.setColumn15("setColumn15" + i);
            student.setColumn16("setColumn16" + i);
            student.setColumn17("setColumn17" + i);
            student.setColumn18("setColumn18" + i);
            student.setColumn19("setColumn19" + i);
            student.setColumn20("setColumn20" + i);
            student.setColumn21("setColumn21" + i);
            student.setColumn22("setColumn22" + i);
            student.setColumn23("setColumn23" + i);
            student.setColumn24("setColumn24" + i);
            student.setColumn25("setColumn25" + i);
            student.setColumn26("setColumn26" + i);
            student.setColumn27("setColumn27" + i);
            student.setColumn28("setColumn28" + i);
            student.setColumn29("setColumn29" + i);
            student.setColumn30("setColumn30" + i);
            list.add(student);
        }
        return list;
    }

    @SneakyThrows
    @GetMapping("/batchTest")
    public String batchTest() {

        long count = studentService.count();
        CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(() -> {
            List<Student> list1 = buildList();
            long l11 = System.currentTimeMillis();
            studentService.mySaveBatch(list1);
            long l12 = System.currentTimeMillis();
            return l12 - l11;
        });

        CompletableFuture<Long> future2 = CompletableFuture.supplyAsync(() -> {
            long l21 = System.currentTimeMillis();
            List<Student> list2 = buildList();
            studentService.saveBatch(list2);
            long l22 = System.currentTimeMillis();
            return l22 - l21;
        });

        CompletableFuture<Long> future3 = CompletableFuture.supplyAsync(() -> {
            long l31 = System.currentTimeMillis();
            List<Student> list3 = buildList();
            studentMapper.insertBatchSomeColumn(list3);
            long l32 = System.currentTimeMillis();
            return l32 - l31;
        });

        CompletableFuture<Long> future4 = CompletableFuture.supplyAsync(() -> {
            long l41 = System.currentTimeMillis();
            List<Student> list4 = buildList();
            studentService.saveBatchByJDBC(list4);
            long l42 = System.currentTimeMillis();
            return l42 - l41;
        });

        CompletableFuture.allOf(future1, future2, future3, future4).get();

        System.out.println("student 表数据总量：" + count);
        System.out.println("mySaveBatch-values耗时：" + future1.get());
        System.out.println("MyBatisPlus-SaveBatch耗时：" + future2.get());
        System.out.println("MyBatisPlus-insertBatchSomeColumn耗时：" + future3.get());
        System.out.println("jdbc batch耗时：" + future4.get());
        return "ok";
    }

    @GetMapping("/like")
    public String likeTest(@RequestParam String name) {
        List<Student> list = studentService
                .list(new LambdaQueryWrapper<Student>().likeLeft(Student::getName, name));
        return JSONUtil.toJsonStr(list);
    }
}
